package cn.tedu._05springaop.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 切面类,为 其他类增加额外功能
 * 标识为bean的四个注解:@Component @Service @Controller @Repository
 */
@Aspect
// @Order(10)切面优先级,数字越大优先级越小
@Order(10)
@Component
public class LogAspect {
    @Pointcut("execution(public int cn.tedu._05springaop.aop.CalculatorImpl.*(..))")
    public void doTime() {
    }

    /**
     * 权限标识符
     *     切入点表达式 表示要拦截的方法
     *         @Before("execution(* cn.tedu._05springaop.aop.CalculatorImpl.*(..))")
     *     @Before("execution(public int cn.tedu._05springaop.aop.CalculatorImpl.add(int,int))")
     * - 权限标识符:   *标识不限
     * - 方法返回值： * 表示返回值不限；
     * - 包名部分：    ..  表示包的层次和深度任意；
     * - 类名部分：    *   表示类名任意；
     * - 方法名部分：* 表示方法名任意；
     * - 方法参数列表部分：(..) 表示参数列表任意；
     */

    /**
     * 五种通知类型:
     * 1.前置通知:在目标方法执行前执行 @Before
     * 2.后置通知:在目标方法执行后执行,不管目标方法有没有异常 @After 没有权限获取返回值
     * 3.环绕通知:在目标方法执行前和后执行,不管目标方法有没有异常 @Around
     * 4.异常通知:在目标方法执行过程中,如果出现异常,执行异常通知 @AfterThrowing
     * 5.最终通知:在目标方法执行后执行,不管目标方法有没有异常,最终通知 @AfterReturning
     * 连接点对象 :JoinPoint
     */
    @Before("doTime()")
    public void before(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("[前置通知]:" + methodName + "方法开始,参数为.." + Arrays.toString(args));
    }


    @After("doTime()")
    public void after(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        System.out.println("[后置通知]:" + methodName + "方法结束");
    }

    @AfterReturning(value = "doTime()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
//        Object[] args = joinPoint.getArgs();
        System.out.println("[返回(最终)通知]:" + methodName + "方法返回" + result);
    }

    @AfterThrowing(value = "doTime()", throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint, Exception exception) {
        String methodName = joinPoint.getSignature().getName();
//        Object[] args = joinPoint.getArgs();
        System.out.println("[异常通知]:" + methodName + "方法异常" + exception.getMessage());
    }

    //    @Around("execution(public int cn.tedu._05springaop.aop.CalculatorImpl.*(..))")
//    public void around(JoinPoint joinPoint) {
//        String methodName = joinPoint.getSignature().getName();
//        Object[] args = joinPoint.getArgs();
//        System.out.println("[环绕通知]:" + methodName + "方法开始,参数为.." + Arrays.toString(args));
//    }

    /**
     *ProceedingJoinPoint 继承了JoinPoint
     */
    @Around("doTime()")
    public Object around(ProceedingJoinPoint joinPoint) {
        Object  result = null;
        try {
            System.out.println("环绕通知-前置");
            result = joinPoint.proceed();
            System.out.println("环绕通知-返回");
        }catch (Throwable e){
            System.out.println("环绕通知-异常");
            throw new RuntimeException(e);
        }finally {
            System.out.println("环绕通知-后置");
        }

        return result;
    }

}
